package com.chenyun.satoken.starter.interceptor;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.BackResultException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.same.SaSameUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import com.chenyun.common.constants.AuthConstant;
import com.chenyun.common.constants.SaTokenConstant;
import com.chenyun.common.context.SpringContext;
import com.chenyun.common.context.UserContextHolder;
import com.chenyun.common.event.TokenResolveEvent;
import com.chenyun.satoken.starter.util.SaUtil;
import com.chenyun.satoken.starter.util.SatokenContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * satoken自定义注解拦截器
 * 支持多账户方式
 *
 * @author Lonni
 * @date 2022-11-19 14:05
 */

public class SaMultipleAccountInterceptor implements HandlerInterceptor {

    private final Logger log = LoggerFactory.getLogger(SaMultipleAccountInterceptor.class);
    public boolean isAnnotation = true;
    public SaParamFunction<Object> auth = (handler) -> {
    };

    public SaMultipleAccountInterceptor() {
    }

    public SaMultipleAccountInterceptor(SaParamFunction<Object> auth) {
        this.auth = auth;
    }

    public SaMultipleAccountInterceptor isAnnotation(boolean isAnnotation) {
        this.isAnnotation = isAnnotation;
        return this;
    }

    public SaMultipleAccountInterceptor setAuth(SaParamFunction<Object> auth) {
        this.auth = auth;
        return this;
    }


    /**
     * 重写进入之前的方法
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入自定义的注解校验拦截器");
        try {

            //判断是否是feign请求,如果是,不校验
            if ("true".equals(request.getHeader(SaTokenConstant.FEIGN_CLIENT_KEY))) {
                //feign 拿到uid和tid
                String idToken=request.getHeader(SaSameUtil.SAME_TOKEN);
                String token=request.getHeader(AuthConstant.JWT_TOKEN_HEADER);
                SaUtil.rpcCheck(idToken,token);
                return true;
            }
            //赋值loginType
            log.info("赋值loginType到上下文");
            SaUtil.getLoginType();
            //判断是否需要校验
            if (this.isAnnotation && handler instanceof HandlerMethod) {
                Method method = ((HandlerMethod) handler).getMethod();
                if ((Boolean) SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
                    return true;
                }
                //设置为自定义校验
                SaUtil.checkMultipleAccountElementAnnotation.accept(method.getDeclaringClass());
                SaUtil.checkMultipleAccountElementAnnotation.accept(method);
            }
            this.auth.run(handler);
            //调用satoken的stop函数会直接退出导致以下不能执行! 发布token解析成功事件

        } catch (StopMatchException var5) {
        } catch (BackResultException var6) {
            if (response.getContentType() == null) {
                response.setContentType("text/plain; charset=utf-8");
            }
            response.getWriter().print(var6.getMessage());
            return false;
        }

        log.info("---------------");
        return true;

    }


    /**
     * 请求结束后清空上下文,防止内存泄漏
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        log.info("token拦截器处理完成,清空上下文");
        UserContextHolder.removeUser();
        SatokenContext.removeLoginType();
        //发布销毁事件
        SpringContext.publishEvent(new TokenResolveEvent(this, false));

    }


}
